/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 stationary_combat_object.cpp

	$Header: /heroes4/stationary_combat_object.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "stationary_combat_object.h"

#include <cassert>
#include <cstdlib>

#include "attackable_obstacle.h"
#include "battlefield.h"
#include "combat_object_model.h"
#include "combat_object_model_cache.h"
#include "combat_object_type.h"
#include "combat_object_visitor.h"
#include "combat_reader.h"
#include "compound_object.h"
#include "compound_object_model.h"
#include "strip_resource_name.h"

#include "debug_message.h"
// -----------------------------------------------------------------------
// t_stationary_combat_object members
// -----------------------------------------------------------------------

t_stationary_combat_object::t_stationary_combat_object( t_battlefield& battlefield,
													    t_combat_object_model_cache const&
														model_cache  )
						  : t_idle_processor( 1 ), t_abstract_combat_object(&battlefield),
						    m_battlefield( battlefield )

{
	m_model = model_cache.get();
	m_model_name = model_cache.get_name();
	assert( m_model->get_segments().size() == 1 );
	m_segment = m_model->get_segments().front();
	m_frame = 0;
	assert( m_segment != 0 );
	if (m_segment->get_frame_count() > 1)
	{
		set_delay( m_segment->get_ticks_per_frame() );
		resume_idle_processing();
	}
}

// -----------------------------------------------------------------------
// t_stationary_combat_object members
// -----------------------------------------------------------------------
t_stationary_combat_object::t_stationary_combat_object( t_battlefield& battlefield,
													    t_compound_object*   object )
						  : t_idle_processor( 1 ), t_abstract_combat_object( &battlefield ),
						    m_battlefield( battlefield )
{
	m_compound_object = object;
	m_segment = 0;
	m_frame = 0;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_stationary_combat_object::~t_stationary_combat_object()
{
}


bool t_stationary_combat_object::can_be_destroyed() const
{
	return m_segment->can_be_destroyed();
}

t_combat_object_type t_stationary_combat_object::get_object_type() const
{
	return k_combat_object_type_stationary;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_stationary_combat_object::draw_shadow_to(
	t_uint32				current_time,
	t_screen_rect const &	source_rect,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point ) const
{
	m_segment->draw_shadow_to( m_frame, source_rect, dest, dest_point, get_alpha() );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_stationary_combat_object::draw_shadow_to(
	t_uint32				current_time,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point ) const
{
	m_segment->draw_shadow_to( m_frame, dest, dest_point, get_alpha() );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_stationary_combat_object::draw_to(
	t_uint32				current_time,
	t_screen_rect const &	source_rect,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point ) const
{
	m_segment->draw_to( m_frame, source_rect, dest, dest_point, get_alpha() );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_stationary_combat_object::draw_to(
	t_uint32				current_time,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point ) const
{
	m_segment->draw_to( m_frame, dest, dest_point, get_alpha() );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_stationary_combat_object::can_be_attacked() const
{
	t_attackable_obstacle* obstacle;

	obstacle = dynamic_cast<t_attackable_obstacle*>(m_compound_object.get());
	if (obstacle == 0)
		return false;
	if (is_underlay())
		return false;
	return obstacle->is_alive();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_stationary_combat_object::is_permanent() const
{
	t_attackable_obstacle* obstacle 
		= dynamic_cast<t_attackable_obstacle*>(m_compound_object.get());
	return (obstacle == 0);
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_attackable_object* t_stationary_combat_object::get_attackable_object()
{
	return dynamic_cast<t_attackable_obstacle*>(m_compound_object.get());
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_compound_object* t_stationary_combat_object::get_compound_object() const
{
	return m_compound_object;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
int t_stationary_combat_object::get_footprint_size() const
{
	return m_segment->get_footprint_size();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
int t_stationary_combat_object::get_height() const
{
	return m_segment->get_height();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_screen_rect t_stationary_combat_object::get_rect() const
{
	return m_segment->get_rect();;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_screen_rect t_stationary_combat_object::get_rect( t_uint32 current_time ) const
{
	return m_segment->get_rect( m_frame );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_screen_rect t_stationary_combat_object::get_shadow_rect() const
{
	return m_segment->get_shadow_rect();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_screen_rect t_stationary_combat_object::get_shadow_rect( t_uint32 current_time ) const
{
	return m_segment->get_shadow_rect( m_frame );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_stationary_combat_object::hit_test( t_uint32					current_time,
										   t_screen_point const&	point ) const
{
	return m_segment->hit_test( m_frame, point );
}

bool t_stationary_combat_object::blocks_movement() const
{
	return m_segment->blocks_movement();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_stationary_combat_object::is_animated() const
{
	return m_segment->get_frame_count() > 1;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_stationary_combat_object::is_underlay() const
{
	if (m_compound_object != 0)
		return m_compound_object->is_underlay();
	return m_segment->is_underlay();
}

bool t_stationary_combat_object::obscures_vision() const
{
	return m_segment->obscures_vision();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_stationary_combat_object::needs_redrawing(
	t_uint32	last_update_time,
	t_uint32	current_time ) const
{
	return false;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_stationary_combat_object::set_frame( int arg )
{
	m_battlefield.invalidate( *this );
	m_frame = arg;
	m_battlefield.invalidate( *this );
}

void t_stationary_combat_object::increment_frame()
{
	int frame_count;

	m_battlefield.invalidate( *this );
	m_frame++;
	frame_count = m_segment->get_frame_count();
	if (m_frame >= frame_count)
		m_frame = 0;
	m_battlefield.invalidate( *this );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_stationary_combat_object::on_idle()
{
	increment_frame();
}
		
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_stationary_combat_object::on_removed()
{
	if (m_compound_object != 0)
		m_compound_object->remove( m_battlefield );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_stationary_combat_object::hinders_movement() const
{
	return m_segment->hinders_movement();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_obstacle_type	t_stationary_combat_object::get_obstacle_type() const
{
	return m_segment->get_type();
}

// -----------------------------------------------------------------------
// get relative position for drawing order
// -----------------------------------------------------------------------
int	t_stationary_combat_object::get_depth() const
{
	int result = t_abstract_combat_object::get_depth();

	// force open castle gates to draw under everything, period.
	if (get_obstacle_type() == k_obstacle_castle_gate && is_underlay())
		result += k_depth_underlay;
	return result;
}

// -----------------------------------------------------------------------
// read from saved game
// -----------------------------------------------------------------------
static int const k_current_version = 0;

bool t_stationary_combat_object::read( t_combat_reader& reader )
{
	std::streambuf& stream = reader.get_stream();

	int version = get<t_uint16>( stream );

	if (version < 0 || version > k_current_version)
		return false;
	if (!read_data( stream ))
		return false;

	t_battlefield& battlefield = reader.get_battlefield();

	m_frame = get<t_int16>( stream );
	if (!read_string16( stream, m_model_name ))
		return false;
	if (!m_model_name.empty())
	{
		t_combat_object_model_cache cache;

		cache = get_combat_object_model_cache( m_model_name, battlefield.get_model_scale() );
		m_model_name = cache.get_name();
		m_model = cache.get();
		m_frame = 0;
		m_segment = m_model->get_segments().front();
		if (m_segment->get_frame_count() > 1)
		{
			set_delay( m_segment->get_ticks_per_frame() );
			resume_idle_processing();
		}
	}
	m_compound_object = reader.read_compound_object();
	return true;
}

bool t_stationary_combat_object::write( t_combat_writer& writer ) const
{
	std::streambuf& stream = writer.get_stream();

	put<t_int16>( stream, k_current_version );
	if (!write_data( stream ))
		return false;
	put<t_int16>( stream, m_frame );

	std::string	model_name = m_model_name;

	if (!model_name.empty())
		model_name = strip_type_and_extension( model_name );
	write_string16( stream, model_name );
	writer.write( m_compound_object );
	return true;
}

void t_stationary_combat_object::place_during_read( t_battlefield& battlefield )
{
	if (m_compound_object != 0)
		m_compound_object->place_segment( battlefield, this );
	else
	{
		battlefield.place_object( this, get_position() );
		assert( get_list_position()._Mynode() != NULL );
	}
}

void t_stationary_combat_object::on_placed()
{
	assert( m_segment != 0 );
}
